/*
 * @(#)GenericInvoker.java Dec 25, 2011 - 12:38:08 PM
 * 
 * Copyright 2011 Cin/UFPE/VSPEED
 */
package speed2010.andreza.vspeed.integration;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import speed2010.application.gui.VirtualNodeInfo;
import speed2010.mensagem.Mensagem;
import speed2010.mensagem.MsgTipo;
import speed2010.mensagem.TipoDaFila;
import speed2010.middleware.InvokerP;
import speed2010.middleware.comunicacao.Address;
import speed2010.middleware.serviceOverlay.node.Node;
import speed2010.middleware.serviceOverlay.node.NodeServidorDeNomes;
import speed2010.reformulacao.query.QueryResult;
import speed2010.simulacao.Constantes;
import speed2010.util.SpeedClusterOntology;
import speed2010.util.SpeedLocalOntology;
import speed2010.util.Util;

/**
 * @author <a href="mailto:andreza.lba@gmail.com">Andreza Leite de Alencar</a>
 */
public class InvokerGeneric extends InvokerP {
	private final NodeGeneric nodeGeneric;

	public InvokerGeneric(final Node node) {
		super(node);
		this.nodeGeneric = (NodeGeneric) node;
	}

	@Override
	public void processaMensagens() {
		final Mensagem msg = this.nodeGeneric.getMsgQueue().extraiMsg();

		this.nodeGeneric.processaMsgs(msg);
	}

	/**
	 * Processa todas as mensagens da fila de entrada
	 */
	@SuppressWarnings("unchecked")
	public void processaMsgDeEntrada(final Mensagem msgDaEntrada) {
		switch (msgDaEntrada.getTipo()) {
			
			case GET_REGISTER_PD_LIST_REPLY : {
				final Set<Address> registeredPeers = (LinkedHashSet<Address>) msgDaEntrada.getObjetos().get(0);
				CommunicationManager.registeredPeers = registeredPeers;
				
				synchronized (CommunicationManager.mutex) {
					CommunicationManager.mutex.notify();
				}
			}
				break;

			case GET_LOCAL_ONTOLOGY_REPLAY : {
				final SpeedLocalOntology peerOntology = (SpeedLocalOntology) msgDaEntrada.getObjetos().get(0);
				CommunicationManager.sourceOntology = peerOntology;

				synchronized (CommunicationManager.mutex) {
					CommunicationManager.mutex.notify();
				}
			}
				break;
			
			case GET_PI_ADDRESS_REPLY : {
				final Address piAddress = (Address) msgDaEntrada.getObjetos().get(0);
				this.nodeGeneric.setPiAddress(piAddress);

				synchronized (CommunicationManager.mutex) {
					CommunicationManager.mutex.notify();
				}
			}
				break;

			case GET_PS_ADDRESS_REPLY : {
				final Address psAddress = (Address) msgDaEntrada.getObjetos().get(0);
				this.nodeGeneric.setPsAddress(psAddress);

				synchronized (CommunicationManager.mutex) {
					CommunicationManager.mutex.notify();
				}
			}
				break;

			case GET_PI_CLUSTER_ONTOLOGY_REPLY : {
				final SpeedClusterOntology piOntology = (SpeedClusterOntology) msgDaEntrada.getObjetos().get(0);
				CommunicationManager.piOntology = piOntology;

				synchronized (CommunicationManager.mutex) {
					CommunicationManager.mutex.notify();
				}
			}
				break;

			case GET_PI_LIST_REPLY : {
				final List<Address> piAligns = (List<Address>) msgDaEntrada.getObjetos().get(0);

				CommunicationManager.piMapAddress = new HashMap<Address, byte[]>();
				for (final Address piAddress : piAligns) {
					CommunicationManager.piMapAddress.put(piAddress, null);
				}

				synchronized (CommunicationManager.mutex) {
					CommunicationManager.mutex.notify();
				}
			}
				break;

			case QUERY_REPLY : {
				System.out.println("Recebi Resultado (Invoker Generic)");

				try {

					//synchronized (CommunicationManager.class) {
					if (CommunicationManager.queryResults == null){

						CommunicationManager.queryResults = (Map<Address, QueryResult>) msgDaEntrada.getObjetos().get(0);

					} else {

						CommunicationManager.queryResults.putAll((Map<Address,QueryResult>) msgDaEntrada.getObjetos().get(0));

					}

					CommunicationManager.mutex.wait(5000);
					CommunicationManager.mutex.notify();

					//}
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
				break;
		
			case MULTICAST_VIRTUALNODEINFO_ASK : {
				final Vector<String> dadosDeControle = new Vector<String>();
				final long idSolicitacao = Util.geraID();

				final MsgTipo tipoMsg = MsgTipo.MULTICAST_VIRTUALNODEINFO_ASK;
				final TipoDaFila tipoFila = TipoDaFila.SAIDA;
				final Address sourceAddress = this.nodeGeneric.getAddress();
				final Address psAddress = this.nodeGeneric.getPsAddress();

				final CommunicationManager manager = CommunicationManager.getInstance();

				if (psAddress == null) {
					if (this.nodeGeneric.getPiAddress() == null) {
						Address recoveryPIAddress = null;

						if (manager.getLocalPDAddress() == null) {
							recoveryPIAddress = manager.recoveryPIAddress(manager.getAnyAddress());
						} else {
							recoveryPIAddress = manager.recoveryPIAddress(manager.getLocalPDAddress());
						}
						this.nodeGeneric.setPiAddress(recoveryPIAddress);
					}
				}

				final Mensagem msg = new Mensagem(tipoFila, sourceAddress, psAddress, idSolicitacao, tipoMsg,
						dadosDeControle, null);

				this.nodeGeneric.getMsgQueue().adicionaMsg(msg);
			}
				break;
			
			case GET_PS_TOLOGY_REPLY : {
				final Vector<Object> objetos = msgDaEntrada.getObjetos();
				final Hashtable<String, VirtualNodeInfo> mapInfo = (Hashtable<String, VirtualNodeInfo>) objetos.get(0);
				final Hashtable<Integer, Address> mapAddr = (Hashtable<Integer, Address>) objetos.get(1);

				this.nodeGeneric.getMapAddressReduzido_NodeInfo().clear();
				this.nodeGeneric.getMapAddressReduzido_NodeInfo().putAll(mapInfo);

				this.nodeGeneric.getLocal_mapID_Address().clear();
				this.nodeGeneric.getLocal_mapID_Address().putAll(mapAddr);

				synchronized (CommunicationManager.mutex) {
					CommunicationManager.mutex.notify();
				}
			}
				break;
			
			case SERVIDORDENOMES_GET_REPLY : {
				final int id = Integer.parseInt(msgDaEntrada.getDadosDeControle().get(0));

				Address address = null;
				if (msgDaEntrada.getDadosDeControle().get(1).length() > 0) {
					address = new Address(msgDaEntrada.getDadosDeControle().get(1));
				} else {
					System.err.println("ERRO: O id " + id + " não está armazenado no servidor de nomes.");
				}
				if (address != null) {
					if (!this.nodeGeneric.getLocal_mapID_Address().containsKey(id)) {
						this.nodeGeneric.getLocal_mapID_Address().put(id, address);
					} else {
						System.err.println("ERRO: Uma SERVIDORDENOMES_GET_REPLY foi recebida"
								+ " de um id que já estava na lista local");
					}
				}
			}
				break;

		}
	}
	/**
	 * Verifica se o address está no mapeamento local, se não estiver, vai consultar o servidor de nomes
	 * 
	 * @param id
	 * @return
	 */
	synchronized public Address getAddress(final int id) {
		Address retorno = null;
		if (this.nodeGeneric.getLocal_mapID_Address().containsKey(id)) {
			retorno = this.nodeGeneric.getLocal_mapID_Address().get(id);
		} else {
			this.nodeGeneric.sendMsg(NodeServidorDeNomes.address, MsgTipo.SERVIDORDENOMES_GET_ASK);
			while (!this.nodeGeneric.getLocal_mapID_Address().containsKey(id)) {
				try {
					sleep(Constantes.sleepTimeGetAddressFromSN);
				} catch (final InterruptedException e) {
					e.printStackTrace();
				}
			}

			retorno = this.nodeGeneric.getLocal_mapID_Address().get(id);
		}

		return retorno;
	}
}